package utils;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

import ui.Application;
import ui.Constants;

/**
 * ClientConnectionUtil is an utility class responsible for handling
 * communication logic with a host. It makes use of the standard Socket API for
 * connection, disconnecting, sending and receiving messages from a host
 * 
 * @author Cipri Lucaci
 * @author Rustem Bekmukhametov
 * @author Alba Xhani
 */
public class ClientConnectionUtil {
	private static Logger logger = LogManager.getLogger(Application.class.getName()); // Logger object
	
	private Socket endPointSocket = null;
	private java.io.InputStream inStream = null;
	private java.io.OutputStream outStream = null;
	
	/**
	 * Constructor
	 */
	public ClientConnectionUtil() {}

	/**
	 * Establishes the communication
	 * 
	 * @param serverIp
	 *            IP or hostname of the server to connect
	 * @param serverPort
	 *            the port number
	 * @return whether the connection established or not
	 * @throws IOException
	 */
	public boolean initSocketConnection(String serverIp, int serverPort)
			throws IOException, SocketTimeoutException {
		/** create socket, connect to server and init streams **/
		boolean connected = true;
		endPointSocket = new Socket();
		endPointSocket.connect(new InetSocketAddress(serverIp, serverPort), Constants.SETTINGS.SOCKET_TIMEOUT);
		if (endPointSocket.isConnected()) {
			inStream = endPointSocket.getInputStream();
			outStream = endPointSocket.getOutputStream();
			connected = true;
		} else
			connected = false;
		return connected;
	}

	/**
	 * @return whether a connection to a host established or not
	 */
	public boolean isConnectionEstablished() {
		 if (endPointSocket == null)
			 return false; 
		 boolean status = endPointSocket.isConnected() && !endPointSocket.isClosed();
		 return status;
	}

	/**
	 * @return whether there is a response from the server available or not
	 * @throws IOException
	 */
	public boolean isResponseAvailable() throws IOException {
		return inStream.available() > 0;
	}

	/**
	 * Sends bytes to the connected host
	 * 
	 * @param message
	 *            a set of bytes containing the message in ASCII format
	 * @throws IOException
	 */
	public void send(byte[] message) throws IOException {
		if (!this.isConnectionEstablished()) {
			return;
		}
		if (message.length > 0) {
			byte[] messageWrapper = new byte[message.length + 1];
			System.arraycopy(message, 0, messageWrapper, 0, message.length);
			messageWrapper[messageWrapper.length - 1] = 13;
			outStream.write(messageWrapper);
			outStream.flush();
		}
	}

	/**
	 * Tries to receive a message from the server
	 * 
	 * @return a set of bytes representing the message in ASCII
	 * @throws IOException
	 */
	public byte[] receive() throws IOException {
		byte[] messageBytes = new byte[128 * 1024];
		int numberOfBytesRead = inStream.read(messageBytes);
		if (numberOfBytesRead > 0) {
			byte[] ret = new byte[numberOfBytesRead];
			System.arraycopy(messageBytes, 0, ret, 0, ret.length);
			return ret;
		}
		return null;
	}

	/**
	 * Starts listening for the server in a new thread. Use this method when the
	 * connection was established
	 */
	public void listenServerAsync() {
		// serverListener = new Thread(new ServerListener(this));
		// serverListener.start();
	}

	/**
	 * Closes the connection with the host and corresponding input/output
	 * streams
	 * 
	 * @throws IOException
	 */
	public void closeConnection() throws IOException {
		if(inStream!=null)
			inStream.close();
		if(outStream!=null)
			outStream.close();
		if(endPointSocket!=null)
			endPointSocket.close();
	}

}